home *** CD-ROM | disk | FTP | other *** search
/ QRZ! Ham Radio 8 / QRZ Ham Radio Callsign Database - Volume 8.iso / mac / files / t_sys5 / 92052tar.gz / 920528.tar / ftpcli.c < prev    next >
C/C++ Source or Header  |  1992-05-14  |  14KB  |  623 lines

  1. /* @(#) $Header: ftpcli.c,v 1.11 92/05/14 13:20:01 deyke Exp $ */
  2.  
  3. /* Internet FTP client (interactive user)
  4.  * Copyright 1991 Phil Karn, KA9Q
  5.  */
  6. #include <stdio.h>
  7. #include <string.h>
  8. #include "global.h"
  9. #include "mbuf.h"
  10. #include "session.h"
  11. #include "cmdparse.h"
  12. #include "timer.h"
  13. #include "socket.h"
  14. #include "icmp.h"
  15. #include "tcp.h"
  16. #include "ftp.h"
  17. #include "commands.h"
  18. #include "netuser.h"
  19. #include "dirutil.h"
  20.  
  21. static void ftpparse __ARGS((char *line,int len));
  22. static int doascii __ARGS((int argc,char *argv[],void *p));
  23. static int dobinary __ARGS((int argc,char *argv[],void *p));
  24. static int doftpcd __ARGS((int argc,char *argv[],void *p));
  25. static int doget __ARGS((int argc,char *argv[],void *p));
  26. static int dolist __ARGS((int argc,char *argv[],void *p));
  27. static int dols __ARGS((int argc,char *argv[],void *p));
  28. static int domkdir __ARGS((int argc,char *argv[],void *p));
  29. static int doput __ARGS((int argc,char *argv[],void *p));
  30. static int dormdir __ARGS((int argc,char *argv[],void *p));
  31. static int dotype __ARGS((int argc,char *argv[],void *p));
  32. static int ftpsetup __ARGS((struct ftp *ftp,void (*recv )(),void (*send )(),void (*state )()));
  33. static void ftpccs __ARGS((struct tcb *tcb,int old,int new));
  34. static void ftpcds __ARGS((struct tcb *tcb,int old,int new));
  35. static int sndftpmsg __ARGS((struct ftp *ftp,char *fmt,char *arg));
  36.  
  37. static char Notsess[] = "Not an FTP session!\n";
  38. static char cantwrite[] = "Can't write %s\n";
  39. static char cantread[] = "Can't read %s\n";
  40.  
  41. static struct cmds Ftpabort[] = {
  42.     "",             donothing,      0, 0, NULLCHAR,
  43.     "abort",        doabort,        0, 0, NULLCHAR,
  44.     NULLCHAR,       NULLFP,         0, 0, "Only valid command is \"abort\""
  45. };
  46.  
  47. static struct cmds Ftpcmds[] = {
  48.     "",             donothing,      0, 0, NULLCHAR,
  49.     "ascii",        doascii,        0, 0, NULLCHAR,
  50.     "binary",       dobinary,       0, 0, NULLCHAR,
  51.     "cd",           doftpcd,        0, 2, "cd <directory>",
  52.     "dir",          dolist,         0, 0, NULLCHAR,
  53.     "list",         dolist,         0, 0, NULLCHAR,
  54.     "get",          doget,          0, 2, "get <remotefile> <localfile>",
  55.     "ls",           dols,           0, 0, NULLCHAR,
  56.     "mkdir",        domkdir,        0, 2, "mkdir <directory>",
  57.     "nlst",         dols,           0, 0, NULLCHAR,
  58.     "rmdir",        dormdir,        0, 2, "rmdir <directory>",
  59.     "put",          doput,          0, 2, "put <localfile> <remotefile>",
  60.     "type",         dotype,         0, 0, NULLCHAR,
  61.     NULLCHAR,       NULLFP,         0, 0, NULLCHAR,
  62. };
  63.  
  64. /* Handle top-level FTP command */
  65. int
  66. doftp(argc,argv,p)
  67. int argc;
  68. char *argv[];
  69. void *p;
  70. {
  71.     struct session *s;
  72.     struct ftp *ftp;
  73.     struct tcb *tcb;
  74.     struct socket lsocket,fsocket;
  75.  
  76.     lsocket.address = INADDR_ANY;
  77.     lsocket.port = Lport++;
  78.     if((fsocket.address = resolve(argv[1])) == 0){
  79.         printf(Badhost,argv[1]);
  80.         return 1;
  81.     }
  82.     if(argc < 3)
  83.         fsocket.port = IPPORT_FTP;
  84.     else
  85.         fsocket.port = tcp_port_number(argv[2]);
  86.  
  87.     /* Allocate a session control block */
  88.     if((s = newsession()) == NULLSESSION){
  89.         printf("Too many sessions\n");
  90.         return 1;
  91.     }
  92.     Current = s;
  93.     if((s->name = malloc((unsigned)strlen(argv[1])+1)) != NULLCHAR)
  94.         strcpy(s->name,argv[1]);
  95.     s->type = FTP;
  96.     s->parse = ftpparse;
  97.  
  98.     /* Allocate an FTP control block */
  99.     if((ftp = ftp_create(0)) == NULLFTP){
  100.         s->type = FREE;
  101.         printf(Nospace);
  102.         return 1;
  103.     }
  104.     ftp->state = COMMAND_STATE;
  105.     s->cb.ftp = ftp;        /* Downward link */
  106.     ftp->session = s;       /* Upward link */
  107.  
  108.     /* Now open the control connection */
  109.     tcb = open_tcp(&lsocket,&fsocket,TCP_ACTIVE,
  110.         0,ftpccr,NULLVFP,ftpccs,0,(int)ftp);
  111.     ftp->control = tcb;
  112.     go(argc, argv, p);
  113.     return 0;
  114. }
  115. /* Parse user FTP commands */
  116. static void
  117. ftpparse(line,len)
  118. char *line;
  119. int len;
  120. {
  121.     struct mbuf *bp;
  122.  
  123.     if(Current->cb.ftp->state != COMMAND_STATE){
  124.         /* The only command allowed in data transfer state is ABORT */
  125.         if(cmdparse(Ftpabort,line,NULL) == -1){
  126.             printf("Transfer in progress; only ABORT is acceptable\n");
  127.         }
  128.         return;
  129.     }
  130.  
  131.     /* Save it now because cmdparse modifies the original */
  132.     bp = qdata(line,len);
  133.  
  134.     if(cmdparse(Ftpcmds,line,NULL) == -1){
  135.         /* Send it direct */
  136.         if(bp != NULLBUF)
  137.             send_tcp(Current->cb.ftp->control,bp);
  138.         else
  139.             printf(Nospace);
  140.     } else {
  141.         free_p(bp);
  142.     }
  143. }
  144. /* Translate 'cd' to 'cwd' for convenience */
  145. static int
  146. doftpcd(argc,argv,p)
  147. int argc;
  148. char *argv[];
  149. void *p;
  150. {
  151.     register struct ftp *ftp;
  152.  
  153.     ftp = Current->cb.ftp;
  154.     return sndftpmsg(ftp,"CWD %s\r\n",argv[1]);
  155. }
  156. /* Translate 'mkdir' to 'xmkd' for convenience */
  157. static int
  158. domkdir(argc,argv,p)
  159. int argc;
  160. char *argv[];
  161. void *p;
  162. {
  163.     register struct ftp *ftp;
  164.  
  165.     ftp = Current->cb.ftp;
  166.     return sndftpmsg(ftp,"XMKD %s\r\n",argv[1]);
  167. }
  168. /* Translate 'rmdir' to 'xrmd' for convenience */
  169. static int
  170. dormdir(argc,argv,p)
  171. int argc;
  172. char *argv[];
  173. void *p;
  174. {
  175.     register struct ftp *ftp;
  176.  
  177.     ftp = Current->cb.ftp;
  178.     return sndftpmsg(ftp,"XRMD %s\r\n",argv[1]);
  179. }
  180. static int
  181. dobinary(argc,argv,p)
  182. int argc;
  183. char *argv[];
  184. void *p;
  185. {
  186.     char *args[2];
  187.  
  188.     args[1] = "I";
  189.     return dotype(2,args,p);
  190. }
  191. static int
  192. doascii(argc,argv,p)
  193. int argc;
  194. char *argv[];
  195. void *p;
  196. {
  197.     char *args[2];
  198.  
  199.     args[1] = "A";
  200.     return dotype(2,args,p);
  201. }
  202. /* Handle "type" command from user */
  203. static int
  204. dotype(argc,argv,p)
  205. int argc;
  206. char *argv[];
  207. void *p;
  208. {
  209.     register struct ftp *ftp;
  210.  
  211.     ftp = Current->cb.ftp;
  212.     if(ftp == NULLFTP)
  213.         return -1;
  214.     if(argc < 2){
  215.         switch(ftp->type){
  216.         case IMAGE_TYPE:
  217.             printf("Image\n");
  218.             break;
  219.         case ASCII_TYPE:
  220.             printf("Ascii\n");
  221.             break;
  222.         }
  223.         return 0;
  224.     }
  225.     switch(*argv[1]){
  226.     case 'i':
  227.     case 'I':
  228.     case 'b':
  229.     case 'B':
  230.         ftp->type = IMAGE_TYPE;
  231.         sndftpmsg(ftp,"TYPE I\r\n","");
  232.         break;
  233.     case 'a':
  234.     case 'A':
  235.         ftp->type = ASCII_TYPE;
  236.         sndftpmsg(ftp,"TYPE A\r\n","");
  237.         break;
  238.     case 'L':
  239.     case 'l':
  240.         ftp->type = IMAGE_TYPE;
  241.         sndftpmsg(ftp,"TYPE L %s\r\n",argv[2]);
  242.         break;
  243.     default:
  244.         printf("Invalid type %s\n",argv[1]);
  245.         return 1;
  246.     }
  247.     return 0;
  248. }
  249. /* Start receive transfer. Syntax: get <remote name> [<local name>] */
  250. static int
  251. doget(argc,argv,p)
  252. int argc;
  253. char *argv[];
  254. void *p;
  255. {
  256.     char *remotename,*localname;
  257.     register struct ftp *ftp;
  258.     char *mode;
  259.  
  260.     ftp = Current->cb.ftp;
  261.     if(ftp == NULLFTP){
  262.         printf(Notsess);
  263.         return 1;
  264.     }
  265.     if(ftp->fp != NULLFILE && ftp->fp != stdout)
  266.         fclose(ftp->fp);
  267.     ftp->fp = NULLFILE;
  268.  
  269.     remotename = argv[1];
  270.     if(argc < 3)
  271.         localname = remotename;
  272.     else
  273.         localname = argv[2];
  274.  
  275.     if(ftp->type == IMAGE_TYPE)
  276.         mode = WRITE_BINARY;
  277.     else
  278.         mode = "w";
  279.  
  280.     if(!strcmp(localname, "-")){
  281.         ftp->fp = stdout;
  282.     } else if((ftp->fp = fopen(localname,mode)) == NULLFILE){
  283.         printf(cantwrite,localname);
  284.         return 1;
  285.     }
  286.     ftp->state = RECEIVING_STATE;
  287.     ftpsetup(ftp,ftpdr,NULLVFP,ftpcds);
  288.  
  289.     /* Generate the command to start the transfer */
  290.     return sndftpmsg(ftp,"RETR %s\r\n",remotename);
  291. }
  292. /* List remote directory. Syntax: dir <remote files> [<local name>] */
  293. static int
  294. dolist(argc,argv,p)
  295. int argc;
  296. char *argv[];
  297. void *p;
  298. {
  299.     register struct ftp *ftp;
  300.  
  301.     ftp = Current->cb.ftp;
  302.     if(ftp == NULLFTP){
  303.         printf(Notsess);
  304.         return 1;
  305.     }
  306.     if(ftp->fp != NULLFILE && ftp->fp != stdout)
  307.         fclose(ftp->fp);
  308.     ftp->fp = NULLFILE;
  309.  
  310.     if(argc < 3 || !strcmp(argv[2], "-")){
  311.         ftp->fp = stdout;
  312.     } else if((ftp->fp = fopen(argv[2],"w")) == NULLFILE){
  313.         printf(cantwrite,argv[2]);
  314.         return 1;
  315.     }
  316.     ftp->state = RECEIVING_STATE;
  317.     ftpsetup(ftp,ftpdr,NULLVFP,ftpcds);
  318.     /* Generate the command to start the transfer
  319.      * It's done this way to avoid confusing the 4.2 FTP server
  320.      * if there's no argument
  321.      */
  322.     if(argc > 1)
  323.         return sndftpmsg(ftp,"LIST %s\r\n",argv[1]);
  324.     else
  325.         return sndftpmsg(ftp,"LIST\r\n","");
  326. }
  327. /* Abbreviated (name only) list of remote directory.
  328.  * Syntax: ls <remote directory/file> [<local name>]
  329.  */
  330. static int
  331. dols(argc,argv,p)
  332. int argc;
  333. char *argv[];
  334. void *p;
  335. {
  336.     register struct ftp *ftp;
  337.  
  338.     ftp = Current->cb.ftp;
  339.     if(ftp == NULLFTP){
  340.         printf(Notsess);
  341.         return 1;
  342.     }
  343.     if(ftp->fp != NULLFILE && ftp->fp != stdout)
  344.         fclose(ftp->fp);
  345.     ftp->fp = NULLFILE;
  346.  
  347.     if(argc < 3 || !strcmp(argv[2], "-")){
  348.         ftp->fp = stdout;
  349.     } else if((ftp->fp = fopen(argv[2],"w")) == NULLFILE){
  350.         printf(cantwrite,argv[2]);
  351.         return 1;
  352.     }
  353.     ftp->state = RECEIVING_STATE;
  354.     ftpsetup(ftp,ftpdr,NULLVFP,ftpcds);
  355.     /* Generate the command to start the transfer */
  356.     if(argc > 1)
  357.         return sndftpmsg(ftp,"NLST %s\r\n",argv[1]);
  358.     else
  359.         return sndftpmsg(ftp,"NLST\r\n","");
  360. }
  361. /* Start transmit. Syntax: put <local name> [<remote name>] */
  362. static int
  363. doput(argc,argv,p)
  364. int argc;
  365. char *argv[];
  366. void *p;
  367. {
  368.     char *remotename,*localname;
  369.     char *mode;
  370.     struct ftp *ftp;
  371.  
  372.     if((ftp = Current->cb.ftp) == NULLFTP){
  373.         printf(Notsess);
  374.         return 1;
  375.     }
  376.     localname = argv[1];
  377.     if(argc < 3)
  378.         remotename = localname;
  379.     else
  380.         remotename = argv[2];
  381.  
  382.     if(ftp->fp != NULLFILE && ftp->fp != stdout)
  383.         fclose(ftp->fp);
  384.  
  385.     if(ftp->type == IMAGE_TYPE)
  386.         mode = READ_BINARY;
  387.     else
  388.         mode = "r";
  389.  
  390.     if((ftp->fp = fopen(localname,mode)) == NULLFILE){
  391.         printf(cantread,localname);
  392.         return 1;
  393.     }
  394.     ftp->state = SENDING_STATE;
  395.     ftpsetup(ftp,NULLVFP,ftpdt,ftpcds);
  396.  
  397.     /* Generate the command to start the transfer */
  398.     return sndftpmsg(ftp,"STOR %s\r\n",remotename);
  399. }
  400. /* Abort a GET or PUT operation in progress. Note: this will leave
  401.  * the partial file on the local or remote system
  402.  */
  403. doabort(argc,argv,p)
  404. int argc;
  405. char *argv[];
  406. void *p;
  407. {
  408.     register struct ftp *ftp;
  409.  
  410.     ftp = Current->cb.ftp;
  411.  
  412.     /* Close the local file */
  413.     if(ftp->fp != NULLFILE && ftp->fp != stdout)
  414.         fclose(ftp->fp);
  415.     ftp->fp = NULLFILE;
  416.  
  417.     switch(ftp->state){
  418.     case SENDING_STATE:
  419.         /* Send a premature EOF.
  420.          * Unfortunately we can't just reset the connection
  421.          * since the remote side might end up waiting forever
  422.          * for us to send something.
  423.          */
  424.         close_tcp(ftp->data);
  425.         printf("Put aborted\n");
  426.         break;
  427.     case RECEIVING_STATE:
  428.         /* Just exterminate the data channel TCB; this will
  429.          * generate a RST on the next data packet which will
  430.          * abort the sender
  431.          */
  432.         del_tcp(ftp->data);
  433.         ftp->data = NULLTCB;
  434.         printf("Get aborted\n");
  435.         break;
  436.     }
  437.     ftp->state = COMMAND_STATE;
  438.     return 0;
  439. }
  440. /* create data port, and send PORT message */
  441. static int
  442. ftpsetup(ftp,recv,send,state)
  443. struct ftp *ftp;
  444. void (*send)();
  445. void (*recv)();
  446. void (*state)();
  447. {
  448.     struct socket lsocket;
  449.     struct mbuf *bp;
  450.  
  451.     lsocket.address = ftp->control->conn.local.address;
  452.     lsocket.port = Lport++;
  453.  
  454.     /* Compose and send PORT a,a,a,a,p,p message */
  455.  
  456.     if((bp = alloc_mbuf(35)) == NULLBUF){   /* 5 more than worst case */
  457.         printf(Nospace);
  458.         return 0;
  459.     }
  460.     /* I know, this looks gross, but it works! */
  461.     sprintf(bp->data,"PORT %u,%u,%u,%u,%u,%u\r\n",
  462.         hibyte(hiword(lsocket.address)),
  463.         lobyte(hiword(lsocket.address)),
  464.         hibyte(loword(lsocket.address)),
  465.         lobyte(loword(lsocket.address)),
  466.         hibyte(lsocket.port),
  467.         lobyte(lsocket.port));
  468.     bp->cnt = strlen(bp->data);
  469.     send_tcp(ftp->control,bp);
  470.  
  471.     /* Post a listen on the data connection */
  472.     ftp->data = open_tcp(&lsocket,NULLSOCK,TCP_PASSIVE,0,
  473.         recv,send,state,0,(int)ftp);
  474.     return 0;
  475. }
  476. /* FTP Client Control channel Receiver upcall routine */
  477. void
  478. ftpccr(tcb,cnt)
  479. register struct tcb *tcb;
  480. int16 cnt;
  481. {
  482.     struct mbuf *bp;
  483.     struct ftp *ftp;
  484.  
  485.     if((ftp = (struct ftp *)tcb->user) == NULLFTP){
  486.         /* Unknown connection; kill it */
  487.         close_tcp(tcb);
  488.         return;
  489.     }
  490.     /* Hold output if we're not the current session */
  491.     if(Mode != CONV_MODE || Current == NULLSESSION || Current->cb.ftp != ftp)
  492.         return;
  493.  
  494.     if(recv_tcp(tcb,&bp,cnt) > 0){
  495.         while(bp != NULLBUF){
  496.             fwrite(bp->data,1,(unsigned)bp->cnt,stdout);
  497.             bp = free_mbuf(bp);
  498.         }
  499.     }
  500. }
  501.  
  502. /* FTP Client Control channel State change upcall routine */
  503. static void
  504. ftpccs(tcb,old,new)
  505. register struct tcb *tcb;
  506. char old,new;
  507. {
  508.     struct ftp *ftp;
  509.     char notify = 0;
  510.  
  511.     /* Can't add a check for unknown connection here, it would loop
  512.      * on a close upcall! We're just careful later on.
  513.      */
  514.     ftp = (struct ftp *)tcb->user;
  515.  
  516.     if(Current != NULLSESSION && Current->cb.ftp == ftp)
  517.         notify = 1;
  518.  
  519.     switch(new){
  520.     case TCP_CLOSE_WAIT:
  521.         if(notify)
  522.             printf("%s\n",Tcpstates[new]);
  523.         close_tcp(tcb);
  524.         break;
  525.     case TCP_CLOSED:    /* heh heh */
  526.         if(notify){
  527.             printf("%s (%s",Tcpstates[new],Tcpreasons[tcb->reason]);
  528.             if(tcb->reason == NETWORK){
  529.                 switch(tcb->type){
  530.                 case ICMP_DEST_UNREACH:
  531.                     printf(": %s unreachable",Unreach[tcb->code]);
  532.                     break;
  533.                 case ICMP_TIME_EXCEED:
  534.                     printf(": %s time exceeded",Exceed[tcb->code]);
  535.                     break;
  536.                 }
  537.             }
  538.             printf(")\n");
  539.             cmdmode();
  540.         }
  541.         del_tcp(tcb);
  542.         if(ftp != NULLFTP)
  543.             ftp_delete(ftp);
  544.         break;
  545.     default:
  546.         if(notify)
  547.             printf("%s\n",Tcpstates[new]);
  548.         break;
  549.     }
  550. }
  551. /* FTP Client Data channel State change upcall handler */
  552. static void
  553. ftpcds(tcb,old,new)
  554. struct tcb *tcb;
  555. char old,new;
  556. {
  557.     struct ftp *ftp;
  558.  
  559.     if((ftp = (struct ftp *)tcb->user) == NULLFTP){
  560.         /* Unknown connection, kill it */
  561.         close_tcp(tcb);
  562.         return;
  563.     }
  564.     switch(new){
  565.     case TCP_FINWAIT2:
  566.     case TCP_TIME_WAIT:
  567.         if(ftp->state == SENDING_STATE){
  568.             /* We've received an ack of our FIN, so
  569.              * return to command mode
  570.              */
  571.             ftp->state = COMMAND_STATE;
  572.             if(Current != NULLSESSION && Current->cb.ftp == ftp){
  573.                 printf("Put complete, %lu bytes sent\n",
  574.                     tcb->snd.una - tcb->iss - 2);
  575.             }
  576.         }
  577.         break;
  578.     case TCP_CLOSE_WAIT:
  579.         close_tcp(tcb);
  580.         if(ftp->state == RECEIVING_STATE){
  581.             /* End of file received on incoming file */
  582. #ifdef  CPM
  583.             if(ftp->type == ASCII_TYPE)
  584.                 putc(CTLZ,ftp->fp);
  585. #endif
  586.             if(ftp->fp != stdout)
  587.                 fclose(ftp->fp);
  588.             ftp->fp = NULLFILE;
  589.             ftp->state = COMMAND_STATE;
  590.             if(Current != NULLSESSION && Current->cb.ftp == ftp){
  591.                 printf("Get complete, %lu bytes received\n",
  592.                     tcb->rcv.nxt - tcb->irs - 2);
  593.             }
  594.         }
  595.         break;
  596.     case TCP_CLOSED:
  597.         ftp->data = NULLTCB;
  598.         del_tcp(tcb);
  599.         break;
  600.     }
  601. }
  602. /* Send a message on the control channel */
  603. /*VARARGS*/
  604. static int
  605. sndftpmsg(ftp,fmt,arg)
  606. struct ftp *ftp;
  607. char *fmt;
  608. char *arg;
  609. {
  610.     struct mbuf *bp;
  611.     int16 len;
  612.  
  613.     len = strlen(fmt) + strlen(arg) + 10;   /* fudge factor */
  614.     if((bp = alloc_mbuf(len)) == NULLBUF){
  615.         printf(Nospace);
  616.         return 1;
  617.     }
  618.     sprintf(bp->data,fmt,arg);
  619.     bp->cnt = strlen(bp->data);
  620.     send_tcp(ftp->control,bp);
  621.     return 0;
  622. }
  623.